package com.qkjia.springselltest.exdemo;

import com.fasterxml.classmate.Annotations;
import jdk.nashorn.internal.runtime.regexp.joni.constants.Arguments;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.Availability;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;

import javax.swing.*;
import javax.validation.constraints.Size;

@ShellComponent//用于限制为候选命令扫描的类集   同@Component
public class MyCommands {//我的命令

    @ShellMethod("将两个整数相加.")//提供命令描述的方法
    public int add(int a, int b) {
        return a + b;
    }
    //测试  add 3 5  ------------->8

    /**
     * 该属性接受多个值。如果您为单个方法设置多个键，则将使用这些不同的别名注册该命令。
     * key 命令键几乎可以包含任何字符，包括空格。但是，在提出名称时，请记住，用户通常会欣赏一致性
     * （即避免将虚线名称与空格名称混合等）
     *
     * @param a
     * @param b
     * @return
     */
    //自定义命令
    @ShellMethod(value = "Add numbers.", key = "sum")
    public int zqkadd(int a, int b) {
        return a + b;
    }
    //测试  sum 3 5  ------------->8

    /**
     * 对于布尔类型参数, 只能使用命名参数方式来赋值, 但是不能指定命名参数的值.
     * <p>
     * 按名称与位置参数调用命令 如上所示，使用 @ShellMethod
     * 修饰方法是创建命令的唯一要求。这样做时，用户可以通过两种可能的方式设置所有方法参数的值：
     * 使用参数键（例如 --arg 值）。这种方法被称为“按名称”参数或没有键，
     * 只需按照它们在方法签名中出现的相同顺序设置参数值（“位置”参数）。
     * 这两种方法可以混合和匹配，命名参数始终优先（因为它们不太容易产生歧义）。因此，给出以下命令
     *
     * @param a
     * @param b
     * @param c
     * @return 打方法名：   顺序  和 --arg 值  两种方式命 名参数始终优先（因为它们不太容易产生歧义）。因此，给出以下命令
     */
/*    @ShellMethod("Display stuff.")//展示
    public String echo(int a, int b, int c) {
        return String.format("You said a=%d, b=%d, c=%d", a, b, c);//你说a=%d, b=%d, c=%d", a, b, c  %d数字格式
    }*/

    //shell:>echo 1 2 3  -------->You said a=1, b=2, c=3
/*
    1这使用位置参数
    shell:>echo 1 2 3
    You said a=1, b=2, c=3

    2这是全副名称参数的示例
    shell:>echo --a 1 --b 2 --c 3
    You said a=1, b=2, c=3

    3可以根据需要重新排列按名称参数
    shell:>echo --b 2 --c 3 --a 1
    You said a=1, b=2, c=3

    4您可以使用两种方法的组合
    shell:>echo --a 1 2 3
    You said a=1, b=2, c=3

    5非名称参数按显示的顺序解决
    shell:>echo 1 --c 3 2
    You said a=1, b=2, c=3



    */

    /**
     * 对于布尔类型参数, 只能使用命名参数方式来赋值, 但是不能指定命名参数的值.
     * <p>
     * <p>
     * 自定义命名参数密钥
     * 自定义命名参数键 如上所示，导出命名参数键的默认策略是使用方法签名的 java 名称并在它前面加上两个破折号
     * (--)。这可以通过两种方式自定义：要更改整个方法的默认前缀，
     * 使用 @ShellMethod 注释的 prefix() 属性以每个参数的方式覆盖整个键，
     * 使用 @ShellOption 注释对参数进行注释。看看下面的例子
     * 请查看以下示例：
     * <p>
     * 命名参数和参数位置无关, 默认需要使用"–"指定参数名称, 可通过在定义命令时@ShellMethod的prefix 属性更改参数前缀.
     * 对于布尔类型参数, 只能使用命名参数方式来赋值, 但是不能指定命名参数的值
     */

    @ShellMethod(value = "Display stuff.", prefix = "-")//展示数据   前缀-   （@ShellMethod 注释的 prefix() 属性以每个参数的方式覆盖整个键，）
    public String zqkecho(int a, int b, @ShellOption("--third") int c) {//用 @ShellOption 注释对参数进行注释
        return String.format("You said a=%d, b=%d, c=%d", a, b, c);
    }
    //测试
/*    shell:>zqkecho 1 2 3
    You said a=1, b=2, c=3

    shell:>zqkecho -a 1 -b 2 3
    You said a=1, b=2, c=3

    shell:>zqkecho -a 1 -b 2 --third 3
    You said a=1, b=2, c=3

    shell:>zqkecho -b 2 --third 3 -a 1
    You said a=1, b=2, c=3
    */

    //    对于这样的设置，可能的参数密钥将是， 和。-a-b--third
    //    可以为单个参数指定几个键。如果是这样，这些将是指定相同参数的相互排斥的方式（因此只能使用其中一个）。
    //    例如，以下是内置帮助命令的签名：

    //    对于这样的设置，可能的参数键是 -a、-b 和 --third。可以为单个参数指定多个键。
    //    如果是这样，这些将是指定相同参数的互斥方式（因此只能使用其中之一）。例如，这里是内置帮助命令的签名：
 /*   @ShellMethod("Describe a command.")//描述一个命令
    public String help(@ShellOption({"-C", "--command"}) String command) {
        //

        return "command" + command;
    }*/

    //     可选参数和默认值
    //Spring Shell 提供提供参数默认值的能力，这将允许用户省略这些参数
    @ShellMethod("Say hello.")//属性描述
    public String greet(@ShellOption(defaultValue = "World") String who) {//用 @ShellOption 注释对参数进行注释
        return "Hello " + who;
    }
    //测试shell:>greet
    //Hello World


    /**
     * 一个参数接收多个值
     * 对于数组, 集合等类型变量, 需要使用@ShellOption的arity属性明确指定接收的参数个数, 否则不能正确执行命令.
     * 对于数组, 集合等类型变量, 需要使用@ShellOption的arity属性明确指定接收的参数个数, 否则不能正确执行命令.
     */
    //增加一堆数
    @ShellMethod("Add Numbers.")
    public float zqkadd2(@ShellOption(arity = 3) float[] numbers) {
        return numbers[0] + numbers[1] + numbers[2];
    }
    //shell:>add 1 2 3.3
    //6.3
    //shell:>add --numbers 1 2 3.3
    //6.3
    //使用按名称参数方法时，不应重复键。以下不起作用：
    //
    //shell:>add --numbers 1 --numbers 2 --numbers 3.3


    /**
     * 布尔类型参数
     * 对于布尔类型参数, 相当于设置了arity=0, 只能通过命名参数指定名称来赋值, 且命名参数不能赋值. 赋值会出现异常.
     * <p>
     * #----------------------- 指定命名参数即为true, 无须赋值. 赋值会出现异常
     * sshell:>shutdown 30 --force
     * force: true, seconds:30
     * shell:>shutdown --force 30
     * force: true, seconds:30
     * <p>
     * # ------------------------不指定命名参数, 即为false
     * shell:>shutdown 30
     * force: false, seconds:30
     */
    @ShellMethod("Terminate the system.")
    public String shutdown(boolean force) {
        return "You said " + force;
    }
    //测试
   /* 这允许以下调用：

    shell:>shutdown
    You said false
    shell:>shutdown --force
    You said true*/


    /**
     这种特殊处理与默认值规范配合得很好。虽然布尔参数的默认值是它们的默认值为 false，但您可以指定其他方式
     （即 @ShellOption(defaultValue="true")）并且行为将被反转（即，不指定参数将导致值为真，并指定标志将导致值为假）}

     这种隐式行为阻止用户指定一个值(例如)。 如果您希望允许这种行为(并放弃标志方法)，
     则强制使用注释:arity()=0shutdown——force true1
     */
    @ShellMethod("Terminate the system.")
    public String shutdown2(@ShellOption(arity=1, defaultValue="false") boolean force) {

        return "You said " + force;
    }
    //这种隐式行为阻止用户指定一个值(例如)。 如果您希望允许这种行为(并放弃标志方法)，
    //测试shell:>shutdown2
    //You said false
    //shell:>shutdown2 --force
    //Error trying to resolve '--force boolean' using [--force]
    //Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.

    @ShellMethod("Terminate the system.")
    public String shutdown4(@ShellOption(arity=1, defaultValue="true") boolean force) {
        return "You said " + force;
    }
    //测试shell:>shutdown4
    //You said true
    //shell:>shutdown4 --force
    //Error trying to resolve '--force boolean' using [--force]
    //Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.

    @ShellMethod("Terminate the system.")
    public String shutdown3(@ShellOption(arity=0, defaultValue="true") boolean force) {
        return "You said " + force;
    }

    /**
     shell:>shutdown3
     You said true
     shell:>shutdown3 --force
     You said false
     */


    /**Quotes 处理 Spring Shell
        接受用户输入并将其标记为单词，在空格字符上拆分。
     如果用户想要提供包含空格的参数值，则需要引用该值。支持单 (') 和双 (") 引号，并且这些引号不会成为值的一部分：
     */
    @ShellMethod("Prints what has been entered.")//方法注释  ，推荐已大写字母开头 ， 。结尾
    public String echo(String what) {
        return "You said " + what;
    }
    /*
    shell:>echo2 "hello"
    You said hello
    shell:>echo2 'hello word'
    You said hello word
    shell:>echo2 zqk1
    You said zqk
    * */

    /*支持单个和双个报价可使用户轻松地将一种类型的报价嵌入值：

    shell:>echo2 "I‘m here!"
    You said I‘m here!
    shell:>echo2 'He said "Hi!" '
    You said He said "Hi!"

    如果用户需要嵌入用于引用整个参数的相同类型的报价，逃生序列使用反斜线 （） 字符：\

    shell:>echo 'I\'m here!'
    You said I'm here!
    shell:>echo "He said \"Hi!\""
    You said He said "Hi!"
    shell:>echo I\'m here!
    You said I'm here!

    在不使用封闭式引号时，也可以逃逸空间字符，例如：
    shell:>echo This\ is\ a\ single\ value
    You said This is a single value
    */

    /**
····与 Shell 交互 Spring Shell 项目构建在 JLine 库之上，因此带来了许多不错的交互特性，本节将详细介绍其中的一些特性。
     首先，Spring Shell 几乎在任何地方都支持 TAB 补全。
     因此，如果有一个 echo 命令并且用户按 e、c、TAB 则 echo 将出现。如果有多个以 ec 开头的命令，则将提示用户选择
     （使用 TAB 或 Shift+TAB 导航，并使用 ENTER 进行选择。）但完成不会在命令键处停止。如果应用程序开发人员注册了适当的
     bean（请参阅提供 TAB 完成建议），它也适用于参数键 (--arg) 甚至参数值。 Spring Shell 应用程序的另一个不错的特性是支持行连续
     。如果命令及其参数太长并且不能很好地显示在屏幕上，用户可以将其分块并用反斜杠 (\) 字符终止一行，然后按 ENTER 并继续下一行
     。提交整个命令后，这将被解析为好像用户在换行符上输入了一个空格。
     */

    //测试
    // shell:>echo This\ is\ a\ single\
    //\value
    //You said This is a single value

    /**
     * 如果用户打开了一个引号(参见引号处理)，并在引号中点击ENTER，也会自动触发行延续:
     * 用户按下这里的ENTER

     最后，Spring Shell 应用程序受益于许多您在使用常规 OS Shell 时可能已经熟悉的键盘快捷键，这些快捷键是从 Emacs 借来的。
     值得注意的快捷键包括 Ctrl+r 执行反向搜索，Ctrl+a 和 Ctrl+e 分别移动到行首和行尾，或者 Esc f 和 Esc b 一次向前（分别向后）移动一个单词。}
     */
    //测试
    // shell:>echo "hello
    //word"
    //You said hello word

    /*
    验证命令参数 Spring Shell 与 Bean 验证 API 集成以支持对命令参数的自动和自记录约束。
        在命令参数上找到的注释以及方法级别的注释将在命令执行之前得到尊重并触发验证。给出以下命令：*/
    @ShellMethod("Change password.")//方法注释  ，推荐已大写字母开头 ， 。结尾
    public String changePassword(@Size(min = 8, max = 40) String password) {
        return "Password successfully set to " + password;
    }
//   您将免费获得此行为： shell:>change-password hello
//   未满足以下约束：--password string ：
//   大小必须介于 8 和 40 之间（您通过了 'hello'）

    //测试    changePassword  必须转为 change-password
  /*  shell:>change-password hello
    The following constraints were not met:
            --password string : 个数必须在8和40之间 (You passed 'hello')

            正确
            shell:>change-password helloshu
            Password successfully set to helloshu
            */



}